//
//  VideoFrameBufferPool.cpp
//  MediaStreamer
//
//  Created by Think on 16/2/14.
//  Copyright © 2016年 Cell. All rights reserved.
//

#include <stdlib.h>
#include "VideoFrameBufferPool.h"
//#include "MediaLog.h"

namespace MediaData {

VideoFrameBufferPool::VideoFrameBufferPool()
{
    videoFrameSize = MAX_VIDEO_FRAME_SIZE;
    
    //LOGD("VideoFrameBufferPool::VideoFrameBufferPool()");

    for(int i=0; i<VIDEOFRAME_BUFFER_NUM;i++)
    {
        MediaStreamer::VideoFrame *videoFrame = new MediaStreamer::VideoFrame;
        videoFrame->data = (uint8_t*)malloc(videoFrameSize);
        if(videoFrame->data == NULL){
            //LOGD("VideoFrameBufferPool videoFrame->data == NULL");
            int i = 0;
            i = 1;
        }
        printf("data:0x%p\n", videoFrame->data);
        videoFrame->frameSize = videoFrameSize;
        mVideoFrameBuffers.push_back(videoFrame);
    }
    pthread_mutex_init(&mLock, NULL);
    
    write_pos = 0;
    read_pos = 0;
    buffer_num = 0;
}

VideoFrameBufferPool::VideoFrameBufferPool(int videoWidth, int videoHeight)
{
    videoFrameSize = videoWidth*videoHeight*3/2;
        
    for(int i=0; i<VIDEOFRAME_BUFFER_NUM;i++)
    {
        MediaStreamer::VideoFrame *videoFrame = new MediaStreamer::VideoFrame;
        videoFrame->data = (uint8_t*)malloc(videoFrameSize);
        videoFrame->frameSize = videoFrameSize;
        mVideoFrameBuffers.push_back(videoFrame);
    }
    pthread_mutex_init(&mLock, NULL);
    
    write_pos = 0;
    read_pos = 0;
    buffer_num = 0;
}

VideoFrameBufferPool::~VideoFrameBufferPool()
{
    flush();
    
    for(vector<MediaStreamer::VideoFrame*>::iterator it = mVideoFrameBuffers.begin(); it != mVideoFrameBuffers.end(); ++it)
    {
        MediaStreamer::VideoFrame* videoFrame = *it;
        
        if (videoFrame!=NULL) {
            if (videoFrame->data!=NULL) {
                free(videoFrame->data);
                videoFrame->data = NULL;
            }
            
            delete videoFrame;
            videoFrame = NULL;
        }
    }
    
    mVideoFrameBuffers.clear();
    
    pthread_mutex_destroy(&mLock);
}

bool VideoFrameBufferPool::push(MediaStreamer::VideoFrame *videoFrame)
{
    if (videoFrame==NULL) return false;
    if (videoFrame->data==NULL) return false;
    
    pthread_mutex_lock(&mLock);
    
    if (buffer_num>=VIDEOFRAME_BUFFER_NUM) {
        // is full
        pthread_mutex_unlock(&mLock);
        return false;
    }
    
    if (write_pos>=VIDEOFRAME_BUFFER_NUM) {
        write_pos = 0;
    }
    
    memcpy(mVideoFrameBuffers[write_pos]->data, videoFrame->data, videoFrame->frameSize);
    mVideoFrameBuffers[write_pos]->frameSize = videoFrame->frameSize;
    mVideoFrameBuffers[write_pos]->pts = videoFrame->pts;
    mVideoFrameBuffers[write_pos]->width = videoFrame->width;
    mVideoFrameBuffers[write_pos]->height = videoFrame->height;
    mVideoFrameBuffers[write_pos]->rotation = videoFrame->rotation;
    mVideoFrameBuffers[write_pos]->videoRawType = videoFrame->videoRawType;
    
    write_pos++;
    buffer_num++;
    
    pthread_mutex_unlock(&mLock);
    
    return true;
}

MediaStreamer::VideoFrame* VideoFrameBufferPool::front()
{
    pthread_mutex_lock(&mLock);
    
    if (buffer_num<=0) {
        // is empty
        pthread_mutex_unlock(&mLock);
        return NULL;
    }else {
        if (read_pos>=VIDEOFRAME_BUFFER_NUM) {
            read_pos = 0;
        }
        int readPos = read_pos;
        pthread_mutex_unlock(&mLock);
        
        return mVideoFrameBuffers[readPos];
    }
}

void VideoFrameBufferPool::pop()
{
    pthread_mutex_lock(&mLock);
    
    memset(mVideoFrameBuffers[read_pos]->data, 0, videoFrameSize);
    mVideoFrameBuffers[read_pos]->frameSize = 0;
    mVideoFrameBuffers[read_pos]->pts = 0;
    mVideoFrameBuffers[read_pos]->width = 0;
    mVideoFrameBuffers[read_pos]->height = 0;
    mVideoFrameBuffers[read_pos]->rotation = 0;
    mVideoFrameBuffers[read_pos]->videoRawType = 0;
    
    read_pos++;
    buffer_num--;
    
    pthread_mutex_unlock(&mLock);
}

void VideoFrameBufferPool::flush()
{
    pthread_mutex_lock(&mLock);
    write_pos = 0;
    read_pos = 0;
    buffer_num = 0;
    pthread_mutex_unlock(&mLock);
}
}
